#include <process.h>

#include <windows.h>
#include <winsock2.h>
#include <sys/timeb.h>

#include "lib.h"

#include "serv.h"
#include "connect.h"
#include "myover.h"
#include "r250.h"
#include "log.h"
#include "ssl.h"
#include "opts.h"

#include "libglob.h"
#include "pathname.h"
#include "rmanager.h"
#include "objbase.h"


#define KEY_SHUTDOWN		0
#define KEY_NEWCLIENT		1
#define KEY_CLIENTIO		2
#define KEY_DEADCLIENT		3
#define KEY_UNPAUSECLIENT	4

#define STARTED_WINSOCK	0x01
#define STARTED_SSL		0x02

#define SELF	Server
#define SUPER	ServerInterface

//TST Random numbers to Skip
#define MAX_NUM_SKIP 99


//20100412 RPF CSN-2671
unsigned long int ticks()
{
unsigned long int tocks;
_timeb currTime; //create a variable of type _timeb
_ftime_s(&currTime); //get the current time

tocks = currTime.time; //currTime.time contains seconds since 1/1/1970
tocks *= 1000; //mulitply by 1000 to convert to ticks
tocks += currTime.millitm; //add in ticks
return tocks; //return # of 1/1000 seconds since 1/1/1970.
} 


VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{  

	Server* s=(Server *)lpParam;
	Connection* probe;

	
    if (lpParam == NULL)
    {    
		Log::msgError("Server::TimerRoutine:  Server Pointer is NULL failed  ");
    }
    else
    {
        probe=s->active;
		while(probe)
		{
			DEBUGMSG( ("Connection Retrieving CampaignMessages\r\n"));
			

			probe->getCampaignMessage();
			probe->processIO(0,0);
			probe = probe->getNext();
					
		}
		
    }

  
}


Server::Server(int port, const String& sAppName)
	: SUPER(port)
{
	server = INVALID_SOCKET;
	stopEvt = shutdown = accepted = randThread = completion = (HANDLE)0;
	worker = (HANDLE*)0;
	startedUp = 0;
	mutex = (PCRITICAL_SECTION)0;
	randMutex = (PCRITICAL_SECTION)0;
	langMutex = (PCRITICAL_SECTION)0;
	randSeed = 0;
	r250 = (R250*)0;
	numWorkers = 1;
	active = dead = (Connection*)0;
	numActive = 0;
	listenNow = (HANDLE)0;
	isListening = false;
	isAccountServer = false; //RPFX 8.4.1

	webServiceCall = NULL;
	
	
	
    // Create our string table language hash table
    m_pLangTable = new HashTable<LangStringTableEntry*>();
	
    // Get our exe dir
    PathName exeDir;
    wchar_t buf[MAX_PATH];
    int32 len = GetModuleFileName((HMODULE)0, buf, MAX_PATH);
    buf[len] = '\0';
    exeDir.set(buf);
    exeDir.removeEnd();
    exeDir.concat("\\");

    // Initialize global resource manager
    gResourceManager = new ResourceManager(exeDir);

	SYSTEM_INFO sysinfo;
	GetSystemInfo(&sysinfo);

    // CLipinski 3/10/06
    // Get num workers dynamically from the server.ini file
    String sINIFile;
    String sServerName;
    ServerOpts::GetOptFileNames( sINIFile, sServerName );
    int workers = GetPrivateProfileInt( sAppName, TEXT("NumWorkers"), 8, sINIFile );
    //RPF20090723 STORE THE APP NAME  
    strAppName = sAppName;
	numWorkers = workers * sysinfo.dwNumberOfProcessors;

	// [AO 2007-06-28] Limit number of worker threads to a value supported by WaitForMultipleObjects()
	if (numWorkers > MAXIMUM_WAIT_OBJECTS)
		numWorkers = MAXIMUM_WAIT_OBJECTS;

	worker = new HANDLE[numWorkers];
	if (!worker)
		return;
	ZeroMemory(worker, numWorkers * sizeof(HANDLE));

	mutex = new CRITICAL_SECTION;
	if (!mutex)
		return;
	ZeroMemory(mutex, sizeof(CRITICAL_SECTION));
	InitializeCriticalSection(mutex);

	randMutex = new CRITICAL_SECTION;
	if (!randMutex)
		return;
	ZeroMemory(randMutex, sizeof(CRITICAL_SECTION));
	InitializeCriticalSection(randMutex);

	langMutex = new CRITICAL_SECTION;
	if (!langMutex)
		return;
	ZeroMemory(langMutex, sizeof(CRITICAL_SECTION));
	InitializeCriticalSection(langMutex);

	//20100412 RPF CSN-2671
    //randSeed = GetTickCount();
    randSeed=ticks();
	r250 = new R250(randSeed);
	if (!r250)
		return;

	completion = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL,
		KEY_SHUTDOWN, 0);
	if (!completion)
		return;

	if (!SSL::setup())
	{
		Log::msgError("Server::init:  SSL::setup failed  ");
		return;
	}
	startedUp |= STARTED_SSL;

	WSADATA wsaData;
	if (0 != WSAStartup(MAKEWORD(2,0), &wsaData))
	{
		Log::msgError("Server::init:  WSAStartup failed (%ld)  ", WSAGetLastError());
		return;
	}
	if (LOBYTE(wsaData.wVersion) != 2 ||
		HIBYTE(wsaData.wVersion) != 0)
	{
		Log::msgError("Server::init:  no valid WinSock 2.0 (%04x)  ",
			wsaData.wVersion);
		WSACleanup();
		return;
	}
	startedUp |= STARTED_WINSOCK;

	stopEvt = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!stopEvt)
		return;

	listenNow = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!listenNow)
		return;

	if (!createWorkers())
		return;
}

Server::~Server()
{
	stop();
	stopWorkers();

	if (listenNow != (HANDLE)0)
	{
		CloseHandle(listenNow);
		listenNow = (HANDLE)0;
	}

	if (stopEvt != (HANDLE)0)
	{
		CloseHandle(stopEvt);
		stopEvt = (HANDLE)0;
	}

	if (startedUp & STARTED_WINSOCK)
	{
		WSACleanup();
		startedUp &= ~STARTED_WINSOCK;
	}

	if (startedUp & STARTED_SSL)
	{
		SSL::teardown();
		startedUp &= ~STARTED_SSL;
	}

	if (langMutex)
	{
		DeleteCriticalSection(langMutex);
		delete langMutex;
		langMutex = (PCRITICAL_SECTION)0;
	}

	if (randMutex)
	{
		DeleteCriticalSection(randMutex);
		delete randMutex;
		randMutex = (PCRITICAL_SECTION)0;
	}

	if (mutex)
	{
		DeleteCriticalSection(mutex);
		delete mutex;
		mutex = (PCRITICAL_SECTION)0;
	}

	delete r250;
	r250 = (R250*)0;

	delete [] worker;
	worker = (HANDLE*)0;

	if (completion)
	{
		CloseHandle(completion);
		completion = (HANDLE)0;
	}

    // **** Cleanup list of language string tables
	// Scope this so the iterator goes out of scope before we destroy the
	//  table.  This is the only place we should really have to do this.
	{
		HashTableIterator<LangStringTableEntry*> it(m_pLangTable);

		for( it.begin(); it.key(); it++ )
		{
			it.value()->m_pStrTblMgr->ReleaseTable( m_sErrorTableName );
            // Delete the memory
			delete it.value();
		}
	}

	// Destroy the actual list
	delete m_pLangTable;

    // Delete global resource manager
    delete gResourceManager;

	// Delete all timers in the timer queue.
    DeleteTimerQueue(mhTimerQueue);

	if (webServiceCall != NULL)
	{
		delete webServiceCall;
		webServiceCall = NULL;
	}

}

//RPFX 8.4.1
void Server::SetAccountServer(bool8 bSet)
{

isAccountServer=bSet;

}


bool8
Server::initSuccess()
{
	return (bool8)(
		stopEvt && listenNow && mutex && randMutex && langMutex && completion && r250
		&&
		shutdown && worker && randThread
		&&
		SUPER::initSuccess());
}

void
Server::run()
{
	DWORD	result, result2, numEvents;
	HANDLE	events[4];
	SOCKET	client;
	WSANETWORKEVENTS ne;
	bool8	isRunning = true;
	uint32	dropDeadTime;
	bool8	bRestartListening = false;

	if (!initSuccess())
		return;

	if (!startWorkers())
		return;
//  RPFX Simple test to push out data witha timer..  
//	if(isAccountServer)
//		if(!startTimer())
//			return;

	SetEvent(listenNow);
	numEvents = 0;
	events[numEvents++] = stopEvt;
	events[numEvents++] = listenNow;

	while (isRunning)
	{
		result = WaitForMultipleObjects(numEvents, events, FALSE, INFINITE);

		switch (result)
		{
			case WAIT_OBJECT_0:			// stopEvt (from external control)
				isRunning = false;
				break;

			case WAIT_OBJECT_0 + 1:		// start listening
				if (!isListening)
				{
					ResetEvent(listenNow);

					if (startListening() &&
						//
						// double check...
						//
						server != INVALID_SOCKET && accepted)
					{
						isListening = true;
						events[numEvents++] = accepted;

						Log::msgInformation("Server::run:  handles: %ld, %ld, %ld;  %ld, %ld, %ld; server: %u",
							events[0], events[1], events[2], 
							stopEvt, listenNow, accepted, server);
					}
					else
					{
						Log::msgError("Server::run:  startListening() failed  ");

						isRunning = false;
					}
				}
				break;

			case WAIT_OBJECT_0 + 2:		// new connection
				EnterCriticalSection(mutex);

				if ((result2 = WSAEnumNetworkEvents(server, accepted, &ne)) == SOCKET_ERROR )
				{
					Log::msgError("Server::run:  WSAEnumNetworkEvents failed "
						"(result: 0x%08x  error: %ld); server handle: %u",
						result2, WSAGetLastError(), server);

					bRestartListening = true;
				}
				else
				{									
					client = accept(server, NULL, NULL);

					if (client != INVALID_SOCKET)
						handleNewClient(client);
					else
					{
						Log::msgError("Server::run:  accept() failed  "
						"(error: %ld); server handle: %u",
						WSAGetLastError(), server);

						bRestartListening = true;
					}
				}

				if (bRestartListening)
				{
					Log::msgError("Server::rerun: Attempting to stop and restart");

					// We don't necessarily know why the accept failed -- lets try and restart the server socket
					stopListening();

					Log::msgError("Server::rerun: Service stopped - attempting to restart");

					if (startListening() &&
						//
						// double check...
						//
						server != INVALID_SOCKET && accepted)
					{
						isListening = true;
						events[numEvents] = accepted;

						Log::msgError("Server::rerun:  handles: %ld, %ld, %ld;  %ld, %ld, %ld; server: %u",
							events[0], events[1], events[2],
							stopEvt, listenNow, accepted, server);
					}
					else
					{
						Log::msgError("Server::rerun:  startListening() failed - stopping service ");

						isRunning = false;
					}

					bRestartListening = false;
				}

				LeaveCriticalSection(mutex);
				break;

			default:
				Log::msgError("Server::run:  wait failed "
					"(result: 0x%08x  error: %ld)  ",
					result, GetLastError());
				Log::msgError("Server::run:  handles: %ld, %ld, %ld;  %ld, %ld, %ld; server: %u",
					events[0], events[1], events[2], 
					stopEvt, listenNow, accepted, server);

				// Need to drop out of loop to avoid an infinite loop
				isRunning = false;
				break;
		}
	}

	//
	// stop everything else (necessary if can't start listening //
	stop();

	stopping = true;

	Log::numUsers(numActive, ServerOpts::appName);

	dropDeadTime = 5 * 60 * 1000;	// 5 minutes for stale connections

	while (1)
	{
		Log::stoppingCheckpoint();

		if (numActive <= 0)
			break;

		if (allStale(dropDeadTime))
			break;

		Sleep(5000);
        
        Log::numUsers(numActive, ServerOpts::appName);
	}

	stopWorkers();
}

void
Server::stop()
{
	EnterCriticalSection(mutex);

	SetEvent(stopEvt);
	stopListening();

	LeaveCriticalSection(mutex);
}

uint32
Server::rand()
{
	uint32 result;

	EnterCriticalSection(randMutex);
    ////////////////////////////////////////////////
    //BACKGROUND CYCLING IMPROVEMENT SUGGESTED BY TST
    //DATE: 07-04-2010 
    //Author:RPF
    ////////////////////////////////////////////////
    int nskip=::rand()%MAX_NUM_SKIP;
    for (int t=0;t<nskip;t++)
       r250->rani();
	result = r250->rani();
	LeaveCriticalSection(randMutex);

	return result;
}

bool8
Server::startListening()
{
	INT err;
	SOCKADDR_IN localAddr;

	// CSN-7499: Expose the version on RTG services
	Log::msgStart();

	server = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (server == INVALID_SOCKET)
	{
		Log::msgError("Server::init:  server socket create failed (%ld)  ",
			WSAGetLastError());
		return false;
	}
	else
		Log::msgInformation("Server::init:  server handle (%u)  ", server);


	ZeroMemory(&localAddr, sizeof(SOCKADDR_IN));
	localAddr.sin_port = htons((u_short)port);
	localAddr.sin_family = AF_INET;

	err = bind(server, (PSOCKADDR)&localAddr, sizeof(SOCKADDR_IN));
	if (err == SOCKET_ERROR)
	{
		Log::msgError("Server::init:  server socket bind failed (%ld)  ",
			WSAGetLastError());
		closesocket(server);
		server = INVALID_SOCKET;
		return false;
	}

    err = listen(server, ServerOpts::nNumListeners);        // kharmon.  12-01-05.  #7665.  Adding option to control the size of the socket listener queue.
	if (err == SOCKET_ERROR)
	{
		Log::msgError("Server::init:  server listen failed (%ld)  ",
			WSAGetLastError());
		closesocket(server);
		server = INVALID_SOCKET;
		return false;
	}

	accepted = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!accepted)
	{
		Log::msgError("Server::init:  accepted CreateEvent failed (%ld)  ",
			WSAGetLastError());
		closesocket(server);
		server = INVALID_SOCKET;
		return false;
	}

	if (SOCKET_ERROR == WSAEventSelect(server, accepted, FD_ACCEPT))
	{
		Log::msgError("Server::init:  WSAEventSelect failed (%ld)  ",
			WSAGetLastError());
		CloseHandle(accepted);
		accepted = (HANDLE)0;
		closesocket(server);
		server = INVALID_SOCKET;
		return false;
	}

	return true;
}

void
Server::stopListening()
{
	if (accepted)
	{
		if (server)
			WSAEventSelect(server, accepted, 0);

		CloseHandle(accepted);
		accepted = (HANDLE)0;
	}
	if (server != INVALID_SOCKET)
	{
		closesocket(server);
		server = INVALID_SOCKET;
	}
}

bool8
Server::createWorkers()
{
	DWORD threadID;
	int32 i, abort;

	shutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (!shutdown)
	{
		numWorkers = 0;
		return false;
	}

	randThread = (HANDLE)_beginthreadex(
		(void*)0, 0, (unsigned(__stdcall*)(void*))randThreadProc,
		(void*)this, 0, (unsigned*)&threadID);
	if (randThread == (HANDLE)0)
	{
		numWorkers = 0;
		CloseHandle(shutdown);
		shutdown = (HANDLE)0;
		return false;
	}

	for (i = 0, abort = -1; abort == -1 && i < numWorkers; i++)
	{
		worker[i] = (HANDLE)_beginthreadex(
			(void*)0, 0, (unsigned(__stdcall*)(void*))workerProc,
			(void*)this, CREATE_SUSPENDED, (unsigned*)&threadID);
		if (worker[i] == (HANDLE)0)
			abort = i;
	}

	if (abort != -1)
	{
		if (SetEvent(shutdown))
		{
			if (WAIT_OBJECT_0 != WaitForSingleObject(randThread, 10000))
			{
				Log::msgError("Server::startWorkers:  WaitFor(randThread) failed  ");
				TerminateThread(randThread, 0);
			}
		}
		else
		{
			Log::msgError("Server::startWorkers:  SetEvent(shutdown) failed  ");
			TerminateThread(randThread, 0);
		}
		CloseHandle(randThread);
		randThread = (HANDLE)0;
		CloseHandle(shutdown);
		shutdown = (HANDLE)0;

		for (i = 0; i < abort; i++)
		{
			TerminateThread(worker[i], 0);
			worker[i] = (HANDLE)0;
		}
		
		for (i = 0; i < abort; i++)
		{
			CloseHandle(worker[i]);
			worker[i] = (HANDLE)0;
		}

		numWorkers = 0;
		return false;
	}

	return true;
}

//RPFX 8.4.1
bool8
Server::startTimer()
{

	HANDLE hTimer = NULL;
   
	 // Create the timer queue.
    mhTimerQueue = CreateTimerQueue();
    if (NULL == mhTimerQueue)
    {
        Log::msgError("Server::startTimer CreateTimerQueue failed");
		return false;

       
    }

	CreateTimerQueueTimer(&hTimer,mhTimerQueue,TimerRoutine,this,60000,1000*30,WT_EXECUTEDEFAULT );
	return true;

}
bool8
Server::startWorkers()
{
	int32 i, abort;

	for (i = 0, abort = -1; abort == -1 && i < numWorkers; i++)
	{
		if (ResumeThread(worker[i]) == -1)
			abort = i;
	}

	if (abort != -1)
	{
		if (SetEvent(shutdown))
		{
			if (WAIT_OBJECT_0 != WaitForSingleObject(randThread, 10000))
			{
				Log::msgError("Server::startWorkers:  WaitFor(randThread) failed  ");
				TerminateThread(randThread, 0);
			}
		}
		else
		{
			Log::msgError("Server::startWorkers:  SetEvent(shutdown) failed  ");
			TerminateThread(randThread, 0);
		}
		CloseHandle(randThread);
		randThread = (HANDLE)0;
		CloseHandle(shutdown);
		shutdown = (HANDLE)0;

		if (abort > 0)
		{
			BOOL postedOk = TRUE;

			for (i = 0; postedOk && i < abort; i++)
				postedOk = PostQueuedCompletionStatus(completion, 0,
					KEY_SHUTDOWN, (LPOVERLAPPED)0);

			if (postedOk)
			{
				if (WAIT_TIMEOUT ==
					WaitForMultipleObjects(abort, worker, TRUE, 10000))
				{
					Log::msgError("Server::startWorkers:  WaitFor(worker) failed  ");
					for (i = 0; i < abort; i++)
					{
						TerminateThread(worker[i], 0);
						worker[i] = (HANDLE)0;
					}
				}
			}
			else
			{
				Log::msgError("Server::startWorkers:  PostQueuedCompletionStatus "
					"failed  (%ld)  ", GetLastError());
				for (i = 0; i < abort; i++)
				{
					TerminateThread(worker[i], 0);
					worker[i] = (HANDLE)0;
				}
			}

			for (i = 0; i < abort; i++)
			{
				CloseHandle(worker[i]);
				worker[i] = (HANDLE)0;
			}
		}
		numWorkers = 0;
		return false;
	}

	return true;
}

void
Server::stopWorkers()
{
	if (worker && completion && numWorkers)
	{
		Connection *conn;
		int32 i;
		BOOL postedOk = TRUE;

		for (i = 0; postedOk && i < numWorkers; i++)
			postedOk = PostQueuedCompletionStatus(completion, 0, KEY_SHUTDOWN,
				(LPOVERLAPPED)0);

		if (postedOk)
		{
			if (WAIT_TIMEOUT ==
				WaitForMultipleObjects(numWorkers, worker, TRUE, 10000))
			{
				Log::msgError("Server::stopWorkers:  WaitFor(worker) failed  ");
				for (i = 0; i < numWorkers; i++)
				{
					TerminateThread(worker[i], 0);
					worker[i] = (HANDLE)0;
				}
			}
		}
		else
		{
			Log::msgError("Server::stopWorkers:  PostQueuedCompletionStatus "
				"failed  (%ld)  ", GetLastError());
			for (i = 0; i < numWorkers; i++)
			{
				TerminateThread(worker[i], 0);
				worker[i] = (HANDLE)0;
			}
		}

		if (SetEvent(shutdown))
		{
			if (WAIT_OBJECT_0 != WaitForSingleObject(randThread, 10000))
			{
				Log::msgError("Server::stopWorkers:  WaitFor(randThread) failed  ");
				TerminateThread(randThread, 0);
			}
		}
		else
		{
			Log::msgError("Server::stopWorkers:  SetEvent(shutdown) failed  ");
			TerminateThread(randThread, 0);
		}

		for (i = 0; i < numWorkers; i++)
		{
			CloseHandle(worker[i]);
			worker[i] = (HANDLE)0;
		}
		numWorkers = 0;

		CloseHandle(randThread);
		randThread = (HANDLE)0;

		while (active)
		{
			conn = active;
			active = active->getNext();
			delete conn;
		}
		numActive = 0;

		while (dead)
		{
			conn = dead;
			dead = dead->getNext();
			delete conn;
		}

		CloseHandle(shutdown);
		shutdown = (HANDLE)0;
	}
}

void
Server::close(Connection* Victim, bool8 allowOneLastBreath)
{
	if (Victim)
	{
		if (!allowOneLastBreath ||
			(allowOneLastBreath && Victim->readyForDeath()))
		{
			if (!PostQueuedCompletionStatus(completion, (DWORD)Victim,
				KEY_DEADCLIENT, (LPOVERLAPPED)0))
			{
				Log::msgError("Server::close:  failed (%ld)  ", GetLastError());

				kill(Victim, false);
			}
		}
	}
}

void
Server::close(Connection* Zombie)
{
	// client wants to close connection, allow it one chance to shutdown
	//
	close(Zombie, true);
}

void
Server::readyForDeath(Connection* Dead)
{
	// client has had a chance to shutdown, kill it
	//
	close(Dead, false);
}

void
Server::unpauseReq(Connection* conn)
{
	if (conn)
	{
		if (!PostQueuedCompletionStatus(completion, (DWORD)conn,
			KEY_UNPAUSECLIENT, (LPOVERLAPPED)0))
		{
			Log::msgError("Server::unpause:  failed (%ld)  ", GetLastError());

			kill(conn, false);
		}
	}
}

void
Server::handleNewClient(SOCKET client)
{
	BOOL keepalive = TRUE;

	WSAEventSelect(client, accepted, 0);

	// use keep alives to detect dead clients
	//
	// MAKE SURE NT Registry key exists:
	//
	//	Key:   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\
	//			  Tcpip\Parameters\KeepAliveTime
	//	Type:  DWORD
	//	Value: 15*60*1000 (15 minutes before keep-alives are sent)
	//	
	// CSN-13249: changing IPPROTO_TCP parameter to SOL_SOCKET
	if (SOCKET_ERROR == setsockopt(client, SOL_SOCKET,
		SO_KEEPALIVE, (char*)&keepalive, sizeof(BOOL)))
	{
		Log::msgError("handleNewClient:  so_keepalive failed (%ld)  ",
			GetLastError());
		//closesocket(client);
		//return;
	}	

	if (!PostQueuedCompletionStatus(completion, (DWORD)client, KEY_NEWCLIENT,
		(LPOVERLAPPED)0))
	{
		Log::msgError("handleNewClient:  Post newclient failed (%ld)  ",
			GetLastError());
		closesocket(client);
	}	
}


void Server::getStringTableName( String& tableName ) const
{
	tableName = TEXT("flash");
}

//////////////////////////////////////////////////////////////////////////////
// LoadStringTableForLanguage
//////////////////////////////////////////////////////////////////////////////
bool8 Server::loadStringTableForLanguage( const String& sLang )
{
    bool8 bRetVal = false;
    LangStringTableEntry* pEntry = NULL;

    // First see if the lang has already been loaded
    if ( m_pLangTable->get( sLang, &pEntry ) )
    {
        bRetVal = true;
    }
    else
    {
		EnterCriticalSection(langMutex);

		// try.catch so we always release the mutex
		try
		{
			// retest under mutex protection just in case another thread
			// loaded the string table just before us
			pEntry = NULL;
			if ( m_pLangTable->get( sLang, &pEntry ) )
			{
				bRetVal = true;
			}
			else
			{
				// We have to load the table ourselves
				pEntry = new LangStringTableEntry(sLang);
				if ( pEntry )
				{
					// Try and load the string table
					getStringTableName( m_sErrorTableName );
					bRetVal = pEntry->m_pStrTblMgr->LoadTableFromResource( m_sErrorTableName );
		            
					// If the load succeeded, add it to the hash table
					if ( bRetVal )
					{
						bRetVal = m_pLangTable->add( sLang, pEntry );
					}

					if ( !bRetVal )
					{
						delete pEntry;
						pEntry = NULL;
					}
				}
			}
		}
		catch(...)
		{
		}

		LeaveCriticalSection(langMutex);
    }

    return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
// getString()
//////////////////////////////////////////////////////////////////////////////
bool8 Server::getString( const String& sLang, const String& sKey, String& sString )
{
    bool8 bRetVal = false;
    LangStringTableEntry* pEntry = NULL;

    // First see if we can get the table
    if ( m_pLangTable->get( sLang, &pEntry ) )
    {
        // Now get the string
        sString = pEntry->m_pStrTblMgr->GetString( sKey );

        // Return success
        bRetVal = true;
    }

    return bRetVal;
}

Connection*
Server::createConnection(SOCKET client)
{
	return new Connection(this, client);
}

bool8 
Server::OnWorkerStarted()
{
    return true;
}

bool8 
Server::OnWorkerStopping()
{
    return true;
}






uint32 __stdcall
Server::workerProc(Server* This)
{
	bool8			workerRunning = true;
	DWORD			xferd, key, result, err;
	Connection* 	conn;
	MyOverlapped*	myover;

    if ( !This->OnWorkerStarted() )
    {
        DEBUGMSG( ("Error, onWorkerStarted() returned false. Stopping worker thread.") );
        workerRunning = false;
    }

    // kharmon.  02-23-07.  Moved this out of the Connection constructor.  Need to
    // have COM initialized on every worker thread...an incoming request may be
    // processed on a thread other than the one the Connection object was created on.
	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	ASSERT( SUCCEEDED( hr ) );
	while (workerRunning)
	{
		
		result = GetQueuedCompletionStatus(This->completion,
		&xferd, &key, (LPOVERLAPPED*)&myover, INFINITE);
			
		err = GetLastError();

		if (result)
		{
			switch (key)
			{
				case KEY_NEWCLIENT:
					conn = This->createConnection((SOCKET)xferd);
					if (conn && conn->initOk())
					{
						HANDLE newComp = CreateIoCompletionPort(
							(HANDLE)xferd, This->completion, KEY_CLIENTIO, 0);
						if (newComp)
						{
							This->completion = newComp;
							This->addActive(conn);
							conn->ready();
                            //20090723 RPF UPDATE AMOUNT PLAYERS ACTIVE ALWAYS 
                            Log::numUsers(This->numActive, ServerOpts::appName);
                           

                            
						}
						else
							delete conn;
					}
					else
						delete conn;
					break;

				case KEY_CLIENTIO:
					myover->conn->processIO(xferd, 0);
					break;

				case KEY_UNPAUSECLIENT:
					conn = (Connection*)xferd;
					conn->readyToUnPause();
					break;

				case KEY_DEADCLIENT:
					conn = (Connection*)xferd;
					This->kill(conn, true);
					break;

				case KEY_SHUTDOWN:
					workerRunning = false;
					break;
			}
		}
		else
		{
			if (myover)		// failed IO (IO packet dequeued)
			{
				switch (key)
				{
					case KEY_NEWCLIENT:
						if ((SOCKET)xferd)
							closesocket((SOCKET)xferd);
						break;

					case KEY_CLIENTIO:
						myover->conn->processIO(0, err);
						break;

					case KEY_UNPAUSECLIENT:
						conn = (Connection*)xferd;
						conn->readyToUnPause();
						break;

					case KEY_DEADCLIENT:
						conn = (Connection*)xferd;
						This->kill(conn, true);
						break;

					case KEY_SHUTDOWN:
						workerRunning = false;
						break;
				}

			}
			else			// other error
			{
				Log::msgError("worker:  GetQueuedCompletionStatus error (%ld)", err);
			}
		}
	}

    This->OnWorkerStopping();

    // kharmon.  02-23-07.  Moved this out of the Connection destructor.  Need to
    // have COM initialized on every worker thread...an incoming request may be
    // processed on a thread other than the one the Connection object was created on.
    CoUninitialize();

	_endthreadex(0);
	return 0;
}

uint32 __stdcall
Server::randThreadProc(Server* This)
{
	DWORD result;

	while (1)
	{
		result = WaitForSingleObject(This->shutdown, 1000);

		if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)	// quit
			break;

		This->rand();
	}

	_endthreadex(0);
	return 0;
}

void
Server::addActive(Connection* conn)
{
	EnterCriticalSection(mutex);
	conn->setPrev((Connection*)0);
	conn->setNext(active);
	if (active)
		active->setPrev(conn);
	active = conn;
	++numActive;
	LeaveCriticalSection(mutex);
}

void
Server::kill(Connection* Zombie, bool8 Flush)
{
	Zombie->finish();

	EnterCriticalSection(mutex);

	if (Zombie->getPrev())
		Zombie->getPrev()->setNext(Zombie->getNext());
	else
		active = Zombie->getNext();
	if (Zombie->getNext())
		Zombie->getNext()->setPrev(Zombie->getPrev());

	Zombie->setPrev((Connection*)0);

	--numActive;
    //20090723 RPF UPDATE AMOUNT PLAYERS ACTIVE ALWAYS 
	//if (stopping)
	Log::numUsers(numActive, ServerOpts::appName);

    // kharmon.  05-16-07.  #19385.  Don't add Zombie to the dead list unless we're not flushing.
    // If we are flushing, we'll delete the Zombie seperately, outside the critical section.  
    // The dead list will almost always be empty, so we won't worry about it.
    if(Flush==false)
	{
        // add zombie to dead list, we'll kill it later.
	    Zombie->setNext(dead);
	    dead = Zombie;
    }
	else 
	{
        // delete connections in the dead list now
        Connection* pCurr=NULL;
		while (dead)
		{
			pCurr = dead;
			dead = dead->getNext();

            // go ahead and do this delete inside the critical section.  This code 
            // will almost never be hit anyway.  Normally the dead list is empty.
			delete pCurr;       
		}
	}

	LeaveCriticalSection(mutex);

    if(Flush==true)
    {
        // kharmon.  05-16-07.  #19385.  Delete this outside the critical section.
        // The destructor may very well involve hitting the database...very slow.
        // We don't want the critical section blocking other threads while we do that.
        delete Zombie;
    }
}
bool8
Server::allStale(uint32 dropDeadTime)
{
	bool8 result = true;
	Connection* probe;

	EnterCriticalSection(mutex);

	probe = active;
	while (result && probe)
	{
		result = probe->isStale(dropDeadTime);
		probe = probe->getNext();
	}

	LeaveCriticalSection(mutex);

	return result;
}
